﻿using System;
using RestSharp;
using Web.Core.EventArgs;
using Ayatta.Contract.Domain;
using System.Collections.Specialized;

namespace Web.Core.OAuths
{
    internal abstract class OAuthClient : IOAuthClient
    {
        protected const string AccessTokenKey = "access_token";
        protected const string RefreshTokenKey = "refresh_token";
        protected const string ExpiresInKey = "expires_in";
        protected const string ScopeKey = "scope";
        protected const string ErrorKey = "error";

        protected IRestClient Client { get; private set; }
        protected OAuth OAuth { get; private set; }

        /// <summary>
        /// State (any additional information that was provided by application and is posted back by service).
        /// </summary>
        public string State { get; private set; }

        public event EventHandler<EventArgs<OAuthUser>> Authorized;

        /// <summary>
        /// Initializes a new instance of the <see cref="OAuthClient"/> class.
        /// </summary>
        /// <param name="oAuth">The auth.</param>
        protected OAuthClient(OAuth oAuth)
        {
            OAuth = oAuth;
            Client = new RestClient(oAuth.BaseUri);
        }

        /// <summary>
        /// 用户成功授权后触发事件
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnAuthorized(EventArgs<OAuthUser> e)
        {
            var handler = Authorized;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// Returns URI of service which should be called in order to start authentication process.
        /// This URI should be used for rendering login link.
        /// </summary>
        /// <remarks>
        /// Any additional information that will be posted back by service.
        /// </remarks>
        public string GetLoginUri(string state = null)
        {
            var request = new RestRequest(OAuth.AuthorizationCodeResource);

            request.AddObject(new {
                response_type = "code",
                client_id = OAuth.ClientId,
                redirect_uri = OAuth.CallbackUri,
                scope = OAuth.Scope,
                state
            });

            return Client.BuildUri(request).ToString();
        }

        /// <summary>
        /// Obtains user information using OAuth2 service and
        /// data provided via callback request.
        /// </summary>
        /// <param name="param">Callback request payload (parameters).</param>
        public MagicKit<OAuthUser> Callback(NameValueCollection param)
        {
            State = param["state"];
            var error = param["error"];
            var result = new MagicKit<OAuthUser>();
            if (!string.IsNullOrEmpty(error))
            {
                result.Message = error;
            }
            var request = new RestRequest(OAuth.AccessTokenResource, Method.POST);
            request.AddObject(new {
                code = param["code"],
                client_id = OAuth.ClientId,
                client_secret = OAuth.PrivateKey,
                redirect_uri = OAuth.CallbackUri,
                grant_type = "authorization_code"
            });

            var response = Client.Execute(request);

            result = Callback(response);

            if (result.Status)
            {
                var guid = Guid.NewGuid().ToString().ToUpper();
                result.Data.Guid = guid;
                result.Data.CreatedOn = DateTime.Now;
                result.Data.CreatedBy = OAuth.Name;
                OnAuthorized(new EventArgs<OAuthUser>(result.Data));
            }
            return result;
        }

        protected abstract MagicKit<OAuthUser> Callback(IRestResponse response);

    }
}